package internal

import (
	"fmt"
	"log"
	"net"
	"runtime"
	"serverlog/src/internal/icode"
	"sync"
	"time"
)

type Client struct {
	//描述
	desc string
	//绑定地址
	addr string
	//绑定端口
	port uint32
	//通道
	notify chan iinternal.INotifyMsg
	//等待组
	wd sync.WaitGroup
	//加个锁保护运行时
	mu sync.Mutex
	//打印标志
	flag int
	//buf
	buf []byte
}

func NewClient(desc string, addr string, port uint32) iinternal.IClient {
	c := &Client{
		desc:   desc,
		addr:   addr,
		port:   port,
		notify: make(chan iinternal.INotifyMsg, 10000),
		wd:     sync.WaitGroup{},
		mu:     sync.Mutex{},
		flag:   log.LstdFlags | log.Lshortfile,
	}

	c.init()

	return c
}

func sendMsg(conn net.Conn, cmd int, msg iinternal.INotifyMsg) error {
	logpkt := NewLogPkt()
	logpkt.Level = uint32(msg.GetLevel())
	logpkt.Msg = []byte(msg.GetMsg())

	if err := logpkt.Pack(); err != nil {
		fmt.Println("logpkt pack err=", err)
		return err
	}
	data := logpkt.GetBuf()
	hp := NewHeadPkt()
	hp.Id = uint32(cmd)
	hp.Len = uint32(len(data))
	hp.Msg = data
	if err := hp.Pack(); err != nil {
		fmt.Println("headpkt pack err=", err)
		return err
	}
	_, err := conn.Write(hp.GetBuf())

	return err
}

func backMsgProc(c *Client) {
	c.wd.Add(1)
	defer c.wd.Done()

	//连接目的服务器
	for  {
		addr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%d", c.addr, c.port))
		if err != nil {
			fmt.Println("resolvetcpaddr err=", err)
			return
		}
		conn, err := net.DialTCP("tcp", nil, addr)
		if err != nil {
			fmt.Println("dialtcp err=", err)
			time.Sleep(time.Second)
			continue
		}

		for msg := range c.notify {
			if sendMsg(conn, iinternal.ID_LEVELLOG, msg) != nil {
				fmt.Println("maybe remote is close now!!!!!!!!!!!!!!!!!!!!")
				break
			}
		}
		fmt.Println("reconnect now!!!!!!!!!!!!!!!!!!!!")
		conn.Close()
	}
}

func (c *Client) init() {
	//开个协程去专门读取msg用以发送
	go backMsgProc(c)
}

func itoa(buf *[]byte, i int, wid int) {
	// Assemble decimal in reverse order.
	var b [20]byte
	bp := len(b) - 1
	for i >= 10 || wid > 1 {
		wid--
		q := i / 10
		b[bp] = byte('0' + i - q*10)
		bp--
		i = q
	}
	// i < 10
	b[bp] = byte('0' + i)
	*buf = append(*buf, b[bp:]...)
}

func (c *Client) formatHeader(buf *[]byte, t time.Time, file string, line int) {
	if c.flag&(log.Ldate|log.Ltime|log.Lmicroseconds) != 0 {
		if c.flag&log.LUTC != 0 {
			t = t.UTC()
		}
		if c.flag&log.Ldate != 0 {
			year, month, day := t.Date()
			itoa(buf, year, 4)
			*buf = append(*buf, '/')
			itoa(buf, int(month), 2)
			*buf = append(*buf, '/')
			itoa(buf, day, 2)
			*buf = append(*buf, ' ')
		}
		if c.flag&(log.Ltime|log.Lmicroseconds) != 0 {
			hour, min, sec := t.Clock()
			itoa(buf, hour, 2)
			*buf = append(*buf, ':')
			itoa(buf, min, 2)
			*buf = append(*buf, ':')
			itoa(buf, sec, 2)
			if c.flag&log.Lmicroseconds != 0 {
				*buf = append(*buf, '.')
				itoa(buf, t.Nanosecond()/1e3, 6)
			}
			*buf = append(*buf, ' ')
		}
	}
	if c.flag&(log.Lshortfile|log.Llongfile) != 0 {
		if c.flag&log.Lshortfile != 0 {
			short := file
			for i := len(file) - 1; i > 0; i-- {
				if file[i] == '/' {
					short = file[i+1:]
					break
				}
			}
			file = short
		}
		*buf = append(*buf, file...)
		*buf = append(*buf, ':')
		itoa(buf, line, -1)
		*buf = append(*buf, ": "...)
	}
}

func (c *Client) Output(calldepth int) error {
	now := time.Now() // get this early.
	var file string
	var line int
	c.mu.Lock()
	defer c.mu.Unlock()
	if c.flag&(log.Lshortfile|log.Llongfile) != 0 {
		c.mu.Unlock()
		var ok bool
		_, file, line, ok = runtime.Caller(calldepth)
		if !ok {
			file = "???"
			line = 0
		}
		c.mu.Lock()
	}
	c.buf = c.buf[:0]
	c.formatHeader(&c.buf, now, file, line)

	return nil
}

func (c *Client) sendMsg(level uint32, msg string) {
	c.Output(3)
	m := NewNotifyMsg()
	m.SetLevel(level)
	ds := fmt.Sprintf("module[%s] =>%s:\t %s", c.desc, string(c.buf), msg)
	if ds[len(ds)-1] != '\n' {
		ds += "\n"
	}
	m.SetMsg(ds)
	select {
	case c.notify <- m:
		break
	default:
		break
	}
}

func (c *Client) Debug(v ...interface{}) {
	s := fmt.Sprint(v...)
	c.sendMsg(2, s)
}

func (c *Client) Info(v ...interface{}) {
	s := fmt.Sprint(v...)
	c.sendMsg(1, s)
}

func (c *Client) Error(v ...interface{}) {
	s := fmt.Sprint(v...)
	c.sendMsg(0, s)
}

func (c *Client) Debugf(format string, v ...interface{}) {

	s := fmt.Sprintf(format, v...)
	c.sendMsg(2, s)
}

func (c *Client) Infof(format string, v ...interface{}) {
	s := fmt.Sprintf(format, v...)
	c.sendMsg(1, s)
}

func (c *Client) Errorf(format string, v ...interface{}) {
	s := fmt.Sprintf(format, v...)
	c.sendMsg(0, s)
}

func (c *Client) Exit() {
	close(c.notify)
	fmt.Println("goto exit!!!!!")
	c.wd.Wait()
	fmt.Println("goto over!!!!!")
}
